package com.openlogic.common.plugins.db.api.config;

import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @ClassName: ImmutablePriorityPropertyDelegate
 * @Description: 具有优先记载顺序的property委托器 env > jvm > properties
 * @author weiyongpeng
 * @date 2024年11月16日 上午9:00:31
 */
public class ImmutablePriorityPropertyDelegate extends ImmutablePropertyDelegate {

	private static final Logger log = LoggerFactory.getLogger(ImmutablePriorityPropertyDelegate.class);

	private static final Map<String, Optional<ConfigValue<String>>> configValueMap = new ConcurrentHashMap<>();

	public ImmutablePriorityPropertyDelegate(String propertyAbsolutePath) {
		super(propertyAbsolutePath);
	}

	@Override
	public String get(String key) {
		Optional<ConfigValue<String>> configValue = configValueMap.computeIfAbsent(key, k -> {
			Optional<ConfigValue<String>> value = getConfigValueFromEnv(key);
			if (value.isPresent()) {
				log.debug("Override config value from env, key: {} actualKey: {}, value: {}", k,
						value.get().getActualKey(), value.get().getValue());
				return value;
			}
			value = getConfigValueFromJvm(key);
			if (value.isPresent()) {
				log.debug("Override config value from jvm, key: {} actualKey: {}, value: {}", k,
						value.get().getActualKey(), value.get().getValue());
				return value;
			}
			value = getConfigValueFromProperties(key);
			value.ifPresent(
					stringConfigValue -> log.debug("Get config value from properties, key: {} actualKey: {}, value: {}",
							k, stringConfigValue.getActualKey(), stringConfigValue.getValue()));
			return value;
		});
		return configValue.map(ConfigValue::getValue).orElse(null);
	}

	@Override
	public String get(String key, String defaultValue) {
		String value = get(key);
		if (value == null) {
			return defaultValue;
		}
		return value;
	}

	@Override
	public Set<String> getPropertyKeys() {
		Set<String> propertyKeys = new HashSet<>();
		propertyKeys.addAll(super.getPropertyKeys());
		propertyKeys.addAll(System.getProperties().stringPropertyNames());
		propertyKeys.addAll(System.getenv().keySet());
		return propertyKeys;
	}

	private Optional<ConfigValue<String>> getConfigValueFromEnv(String key) {
		String value = System.getenv(key);
		if (value != null) {
			return Optional.of(ConfigValue.fromEnv(key, value));
		}
		String envVarKey = String.valueOf(key).replaceAll("[.-]", "_").toUpperCase();
		String envVarVal = System.getenv(envVarKey);
		if (envVarVal != null) {
			return Optional.of(ConfigValue.fromEnv(key, envVarVal));
		}
		return Optional.empty();
	}

	private Optional<ConfigValue<String>> getConfigValueFromJvm(String key) {
		String value = System.getProperty(key);
		if (value != null) {
			return Optional.of(ConfigValue.fromJvm(key, value));
		}
		return Optional.empty();
	}

	private Optional<ConfigValue<String>> getConfigValueFromProperties(String key) {
		String value = super.get(key);
		if (value != null) {
			return Optional.of(ConfigValue.fromProperties(key, value));
		}
		return Optional.empty();
	}

	public static final class ConfigValue<T> {

		private String actualKey;
		private T value;
		private boolean fromProperties;
		private boolean fromJvm;
		private boolean fromEnv;

		public ConfigValue() {
			super();
		}

		public ConfigValue(String actualKey, T value, boolean fromProperties, boolean fromJvm, boolean fromEnv) {
			super();
			this.actualKey = actualKey;
			this.value = value;
			this.fromProperties = fromProperties;
			this.fromJvm = fromJvm;
			this.fromEnv = fromEnv;
		}

		public String getActualKey() {
			return actualKey;
		}

		public void setActualKey(String actualKey) {
			this.actualKey = actualKey;
		}

		public T getValue() {
			return value;
		}

		public void setValue(T value) {
			this.value = value;
		}

		public boolean isFromProperties() {
			return fromProperties;
		}

		public void setFromProperties(boolean fromProperties) {
			this.fromProperties = fromProperties;
		}

		public boolean isFromJvm() {
			return fromJvm;
		}

		public void setFromJvm(boolean fromJvm) {
			this.fromJvm = fromJvm;
		}

		public boolean isFromEnv() {
			return fromEnv;
		}

		public void setFromEnv(boolean fromEnv) {
			this.fromEnv = fromEnv;
		}

		public static <T> ConfigValue<T> fromProperties(String actualKey, T value) {
			return new ConfigValue<>(actualKey, value, true, false, false);
		}

		public static <T> ConfigValue<T> fromJvm(String actualKey, T value) {
			return new ConfigValue<>(actualKey, value, false, true, false);
		}

		public static <T> ConfigValue<T> fromEnv(String actualKey, T value) {
			return new ConfigValue<>(actualKey, value, false, false, true);
		}
	}
}
